#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "util.h"

int main(int argc, char* argv[])
{
        char c;
        char entrada[100], chave[100], aux[100];
        int **mat;
        FILE *input, *ciphertext, *plaintext;
        int size=0, i, idx=0, j, pos, k, aleatorio;
        int plain, key, max, linhas, dif;
        int in_bits[7], key_bits[7], out_bits[7];
        int *posicoes, *texto;

	/* --- gera um número aleatório a ser usado na criptografia com a cifra de vigenere --- */
        srandom(time(NULL));
        aleatorio = random()%50;
	/* ------------------------------------------------------------------------------------- */


        if(argc != 3)
        {
                printf("usage: ./cripto <arquivo de entrada> <palavra_chave>\n");
                exit(-1);
        }      

	/* --- armazena os parâmetros de entrada nas strings entrada e chave --- */
        strcpy(entrada, argv[1]);
        strcpy(chave, argv[2]);
	/* --------------------------------------------------------------------- */

        // max armazena o tamanho da palavra chave
        max = strlen(chave);


        // vetor usado para determinar a posição das colunas na cifra de vigenere
        posicoes = (int*) malloc(max * sizeof(int));


        // size armazena o tamanho do texto de entrada
        size = get_text_size(entrada);
        //decrementa o contador para não considerar o EOF na contagem
        size--;


        // determina a quantidade de linhas necessária na matriz usada na cifra de vigenere
        linhas = size/max;
        if(size%max != 0)
                ++linhas;


        // determina quantas posições precisarão ser preenchidas com '*' na matriz
        dif = (linhas * max) - size;
       
	/* --- alocação de memória --- */
        texto = (int*) malloc((size+dif+1)*sizeof(int));

        mat = (int**) malloc(linhas*sizeof(int*));
        for(i=0; i<linhas; ++i)
                mat[i] = (int*) malloc(max*sizeof(int));
        /* --------------------------- */

        /* --- abre os arquivos --- */
        if(((input = fopen(entrada, "r")) == NULL) || ((ciphertext = fopen("ciphertext.txt", "w")) == NULL) || ((plaintext = fopen("plaintext.txt", "w")) == NULL))
        {
                printf("Erro ao abrir arquivo.\n");
                exit(-1);
        }
        /* ------------------------ */

       


        /* --- criptografa o texto de entrada com a cifra de vigenère e um número aleatório e aplica um xor usando a mesma chave --- */
        pos=0;
        idx=0;
        while((c = fgetc(input)) != EOF)
        {
                // plain recebe o código ascii do caractere a ser criptografado
                plain = (int)c;
                
		// verifica se não é um caractere especial, pois caracteres especiais não são criptografados
                if(plain >= 0)
                {
                        // key recebe a chave para a iteração atual
                        key = (int)chave[idx];
                        
                        // xor
                        binario(key, key_bits);
                        binario(plain, in_bits);
                        xor(key_bits, in_bits, out_bits);
                        plain = decimal(out_bits);

                        // o caractere é criptografado com a cifra de vigenere de forma a permanecer da tabela ascii padrão            
                        plain = plain + key + aleatorio;
                }
               
                // armazena o caractere criptografado no vetor texto
                texto[pos++] = plain;
               
                // a posição da palavra chave é atualizada
                idx = (idx+1)%max;
        }
        /* --------------------------------------------------------------- */
       
        


        /* --- copia o texto para a matriz --- */
        k=0;
        for(i=0; i<linhas; ++i)
                for(j=0; j<max; ++j)
                        mat[i][j] = texto[k++];
       
        for(i=0; i<dif; ++i)
                mat[linhas-1][max-1-i] = 42;
        /* ----------------------------------- */

        

        /* --- copia as letras da chave de forma ordenada em uma string auxiliar --- */
        pos=0;
        for(i=0; i<max; ++i)
        {
                for(j=0; j<pos; ++j)
                        if(chave[i] <= aux[j])
                                break;
                for(k=pos; k>j; --k)
                        aux[k] = aux[k-1];
                aux[j] = chave[i];
                pos++;
        }
        aux[pos] = '\0';        
        /* -------------------------------------------------------------------------- */

        /* --- calcula o vetor de posicoes que mostra a posicao que cada coluna ocupara --- */  
        for(i=0; i<max; ++i)
        {
                for(j=0; j<max; ++j)
                        if(chave[i] == aux[j])
                                break;
                posicoes[i] = j;
        }
        /* --------------------------------------------------------------------------------- */

        

        /* --- aplica a cifra de transposição de colunas --- */
        k=0;
        for(i=0; i<max; ++i)
        {
                for(j=0; j<linhas; ++j)
                        texto[k++] = mat[j][posicoes[i]];
        }
        /* -------------------------------------------------- */

        
        texto[size+dif] = aleatorio;

        // escreve o texto criptografado no ciphertext
        for(i=0; i<(size+dif); ++i)    
                fprintf(ciphertext, "%d\n", texto[i]);




        // volta ao início do ciphertext para que o plaintext possa ser recuperado
        rewind(ciphertext);

        /* --- lê o conteúdo do ciphertext e armazena na string texto --- */
        for(i=0; i<(size+dif+1); ++i)  
                fscanf(ciphertext, "%d\n", &texto[i]);
        /* --------------------------------------------------------------- */

        aleatorio = texto[size+dif];

        
       
        /* --- copia o texto para a matriz na ordem correta das colunas --- */
        k=0;
        for(i=0; i<max; ++i)
                for(j=0; j<linhas; ++j)
                        mat[j][posicoes[i]] = texto[k++];
        /* ---------------------------------------------------------------- */

        /* --- copia o texto decriptografado para a string texto --- */
        k=0;
        for(i=0; i<linhas; ++i)
                for(j=0; j<max; ++j)
                        texto[k++] = mat[i][j];
        /* ---------------------------------------------------------- */

        
               
        /* --- decriptografa a cifra de vigenère e o xor --- */
        pos=0;
        idx=0;
        for(i=0; i<(size); ++i)
        {
                // plain recebe o código ascii do caractere a ser decriptografado
                plain = texto[i];

		// verifica se não é um caractere especial, pois caracteres especiais não foram criptografados e, portanto,
		// não serão decriptografados
                if(plain >= 0)
                {      
                        // key recebe a chave para a iteração atual
                        key = (int)chave[idx];

                        // o caractere é criptografado com a cifra de vigenere de forma a permanecer da tabela ascii padrão            
                        plain = plain - key - aleatorio;

                        // xor
                        binario(key, key_bits);
                        binario(plain, in_bits);
                        xor(key_bits, in_bits, out_bits);
                        plain = decimal(out_bits);
                }
               
                // armazena o caractere decriptografado no vetor texto
                texto[pos++] = plain;
                // a posição da palavra chave é atualizada
                idx = (idx+1)%max;
        }
        /* --------------------------------------------------------------- */

        

        // escreve o texto recuperado no plaintext
        for(i=0; i<size; ++i)
                fprintf(plaintext, "%c", (char)texto[i]);

        /* --- fecha os arquivos --- */
        fclose(input);
        fclose(ciphertext);
        /* ------------------------- */

	/* --- libera a memória alocada dinamicamente --- */
	free(posicoes);
	free(texto);
	for(i=0; i<linhas; ++i)
		free(mat[i]);
	free(mat);
	/* ---------------------------------------------- */

        return 0;
}


